package se.webbzon.boltzmann.game.sequence.builder;

import java.util.ArrayList;
import java.util.List;

import se.webbzon.boltzmann.game.sequence.Sequence;
import se.webbzon.boltzmann.game.sequence.SequenceNode;
import se.webbzon.oschi01.worldarea.WorldArea;

public abstract class AbstractSequenceRow {
	
	// The type name of the sequence row
	private final String name;
	
	// The sequence builder associated with this sequence row
	private final SequenceBuilder builder;
	
	// A list of all the links from sequence row
	private final List<Integer> links;
	
	// The binding of this sequence row
	private AbstractSequenceBinding binding;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new abstract sequence row. **/
	public AbstractSequenceRow(String name, SequenceBuilder builder) {
		this.name = name;
		this.builder = builder;
		links = new ArrayList<Integer>();
		builder.addRow(this);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns a describing text of the sequence row. **/
	public abstract String getText();
	
	/** Returns the maximal number of links. If the maximal number
	 * is negative then an arbitrarily number of links may be added. **/
	public abstract int maxLinks();
	
	/** Returns the minimal number of links. **/
	public abstract int minLinks();
	
	/** Compiles this sequence builder row into a sequence node. **/
	public abstract SequenceNode compile(WorldArea world, Sequence sequence);
	
	/** Binds the given node to all the provided nodes by using the
	 * internal link representation. **/
	public abstract void bind(SequenceNode node, SequenceNode[] nodes);
	
	/** Returns a deflation of the sequence row. **/
	public abstract SequenceRowDeflation getDeflation();
	
	/** Returns the type name of the sequence row. **/
	public String getName() {
		return name;
	}
	
	/** Returns the binding of this sequence row. **/
	public AbstractSequenceBinding getBinding() {
		return binding;
	}
	
	/** Sets the binding of this sequence row. **/
	public void setBinding(AbstractSequenceBinding binding) {
		this.binding = binding;
	}
	
	/** Returns true if the links of this row was set to the given array
	 * of links.**/
	public boolean setLinks(Integer[] links) {
		if (links.length < minLinks() || links.length > maxLinks())
			return false;
		else {
			this.links.clear();
			
			// Check if all links are valid
			for (Integer link : links) {
				if (link != null && (link < 0 || link >= builder.rows()))
					return false;
			}
			
			// Add links to link list
			for (Integer link : links)
				this.links.add(link);
			
			return true;
		}
	}
	
	/** Returns true if the given link was added. The link may be any
	 * number between 0 and the number of rows in the sequence builder
	 * minus one, or null. **/
	public boolean addLink(Integer link) {
		if (link != null && link >= builder.rows())
			return false;
		else if (links.size() < maxLinks() || maxLinks() < 0)
			return links.add(link);
		else
			return false;
	}
	
	/** Returns true if the given link was inserted at the given index. 
	 * The link may be any number between 0 and the number of rows in 
	 * the sequence builder minus one, or null. **/
	public boolean addLink(int index, Integer link) {
		if (link != null && link >= builder.rows())
			return false;
		else if (links.size() < maxLinks() || maxLinks() < 0) {
			links.add(index, link);
			return true;
		} else
			return false;
		
	}
	
	/** Sets the link at the given index to the given link. 
	 * The link may be any number between 0 and the number of rows in 
	 * the sequence builder minus one, or null. **/
	public boolean setLink(int index, Integer link) {
		if (link != null && link >= builder.rows())
			return false;
		else {
			links.set(index, link);
			return true;
		}
	}
	
	/** Returns true if the given link was removed. **/
	public boolean removeLink(Integer link) {
		if (links.size() > minLinks())
			return links.remove(link);
		else
			return false;
	}
	
	/** Returns true if the link with the given index was 
	 * successfully removed. **/
	public boolean removeLink(int index) {
		if (links.size() > minLinks()) {
			links.remove(index);
			return true;
		} else
			return false;
	}
	
	/** Returns the number of links associated with this sequence row. **/
	public int links() {
		return links.size();
	}
	
	/** Returns the link with the given index. **/
	public Integer getLink(int index) {
		return links.get(index);
	}
	
	@Override public String toString() {
		if (links() > 0) {
			StringBuilder builder = new StringBuilder();
			builder.append(getLink(0));
			for (int i = 1; i < links(); i++)
				builder.append("," + getLink(i));
			return getText() + " [" + builder.toString() + "]"; 
		} else 
			return getText();
		
	}

}
